﻿using System.Collections.Generic;
using FreeSwitch.EventSocket.Events;
using FreeSwitch.EventSocket.Events.Channel;

namespace FreeSwitch.EventSocket.CallHandling
{
	[Component]
	public class CallManager
	{
		private readonly EventDispatcher _dispatcher;
		private Dictionary<string, CallCollection> _calls = new Dictionary<string, CallCollection>();

		public CallManager(EventDispatcher dispatcher)
		{
			_dispatcher = dispatcher;
			_dispatcher.EventReceived += OnEvent;
		}

		private void OnEvent(object sender, Events.EventReceivedEventArgs e)
		{
			if (!e.Evt.IsChannelEvent)
				return;

			var channelBase = e.Evt as ChannelBase;
			if (channelBase == null)
				return;

			string userUri = channelBase.ChannelInfo.UserName;
			CallCollection calls = GetCallCollection(userUri);

			Call call;
			if (!calls.TryGetValue(channelBase.UniqueId, out call))
			{
				call = new Call {Id = channelBase.UniqueId};
				calls.Add(call);
			}

			call.UserInfo.Domain = channelBase.ChannelInfo.HostName;
			call.UserInfo.ProfileName = channelBase.ChannelInfo.ProfileName;
			call.UserInfo.UserName = channelBase.ChannelInfo.UserName;
			call.UserInfo.IpAddress = channelBase.Parameters["variable_sip_received_ip"];
			call.State = TranslateCallState(channelBase);


			if (e.Evt is ChannelDestroy)
			{
				calls.Remove(call.Id);
			}
		}

		private CallState TranslateCallState(ChannelBase evt)
		{
			if (evt is ChannelExecute) // and state ExchangeMedia or appname playback
				return CallState.Held;
			if (evt is ChannelExecuteComplete) // and state ExchangeMedia or appname playback
				return CallState.Connected;
			if (evt is ChannelOutgoing)
				return CallState.Offering;
			if (evt is ChannelAnswer)
				return CallState.Connected;
			if (evt is ChannelDestroy)
				return CallState.Idle;
			if (evt is ChannelHangup)
				return CallState.Disconnected;
			if (evt is ChannelPark)
				return CallState.Held;
			if (evt is ChannelProgress)
				return CallState.Alerting; // new in v1.1, CHANNEL_STATE/Routing was used before

			return CallState.Unknown;
		}

		private CallCollection GetCallCollection(string userUri)
		{
			CallCollection calls;
			lock (_calls)
			{
				if (_calls.TryGetValue(userUri, out calls))
					return calls;

				calls = new CallCollection();
				_calls.Add(userUri, calls);
				return calls;
			}
		}
	}
}